refactor(config): add shared _resolve_component utility for plugin loading#5215
Conversation
Extracts the common pattern used by exporter factory functions: check built-in registry → fall back to additional_properties → load via entry point → raise if nothing matched. The pending exporter PR (open-telemetry#5128) will be updated to use this utility, reducing the three near-identical factory functions to one-liners. Assisted-by: Claude Opus 4.6
- Add _ComponentConfig Protocol declaring additional_properties contract - Type registry as dict[str, Callable[[Any], Any]] - Add Any return type annotation Assisted-by: Claude Opus 4.6
…nent' into mike/config-shared-resolve-component
There was a problem hiding this comment.
Pull request overview
Introduces a shared _resolve_component() helper in opentelemetry.sdk._configuration._common that abstracts the registry-then-entry-point resolution pattern used by exporter factory functions for declarative config. This is groundwork for PR #5128 (exporter plugin loading), where it will replace duplicated logic across the three signal-specific factories.
Changes:
- Add
_ComponentConfigProtocol and_resolve_component()utility to_common.pyfor resolving config dataclasses (withadditional_properties) into component instances via either a registry of built-ins or entry-point plugin loading. - Add 5 unit tests covering built-in resolution, plugin loading with/without config, missing component, and missing plugin error paths.
- Add changelog entry.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_common.py | Adds _ComponentConfig Protocol and _resolve_component() helper |
| opentelemetry-sdk/tests/_configuration/test_common.py | Adds TestResolveComponent with five test cases |
| .changelog/5215.added | Changelog entry for the new shared utility |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Thanks @lzchen - I think this PR was opened around the same time as the other PR and that one landed first. I don't think they had the dependency link as described. I'll update the description. |
…wins - Add docstring note explaining the JSON schema enforces exactly one component per config block (minProperties: 1, maxProperties: 1), and that "first match wins" is the defensive fallback if validation is bypassed - Add test documenting that when multiple built-in fields are set, the first registry match wins Assisted-by: Claude Opus 4.6
Values in additional_properties are either nested config dicts (for **kwargs splatting) or None. Document this with a more concrete type annotation. Note: the Protocol's instance-attribute declaration still diverges from the generated model's ClassVar; tracked separately. Assisted-by: Claude Opus 4.6
…ttribute mismatch Adds a docstring note pointing to open-telemetry#5268 which tracks reviewing the generated additional_properties typing for stricter type checks. Assisted-by: Claude Opus 4.6
Description
Adds a shared
_resolve_component()utility in_common.pythat extracts the common pattern used by exporter factory functions for plugin loading.Suggested by @lzchen in #5098.
The pattern
All three exporter factory functions (
_create_span_exporter,_create_log_record_exporter,_create_push_metric_exporter) follow the same structure:additional_propertiesfor plugin names →load_entry_point(group, name)(**(config or {}))ConfigurationErrorif nothing matchedThe utility
Tests
5 tests covering: built-in resolution, plugin loading with config, plugin with empty config, no-component error, plugin-not-found error.